/*
 * Added Global Payment by manit
 */
package com.openbravo.pos.payment;

import com.openbravo.data.loader.LocalRes;
import com.openbravo.pos.forms.AppLocal;
import com.openbravo.pos.forms.AppProperties;
import com.openbravo.pos.util.AltEncrypter;
import java.io.*;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.xml.rpc.ServiceException;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;

public class PaymentGatewayGlobal implements PaymentGateway {

    // private static final String SALE = "SALE";
    // Authorize only
    private static final String SALE = "01";
    private static final String REFUND = "04";
    private String exactID = "";
    private String password = "";
    private static String HOST;
    private final static int PORT = 443;
    private boolean m_bTestMode;
    private static String APPROVED = "true";
    private String AuthType = "01"; // Manupulate this to include tips (Post-Auth 02). 

    public PaymentGatewayGlobal(AppProperties props) {

        exactID = props.getProperty("payment.commerceid");

        AltEncrypter cypher = new AltEncrypter("cypherkey" + props.getProperty("payment.commerceid"));
        this.password = cypher.decrypt(props.getProperty("payment.commercepassword").substring(6));

        this.m_bTestMode = Boolean.valueOf(props.getProperty("payment.testmode")).booleanValue();
        HOST = (m_bTestMode)
                ? "api.demo.globalgatewaye4.firstdata.com"
                : "api.globalgatewaye4.firstdata.com";
        boolean isTip = Boolean.parseBoolean(props.getProperty("machine.showTip"));
        if (!isTip) {
            //If no tip feature, directly process the transaction.
            AuthType = "00";
        }
    }

    //Default Constructor
    public PaymentGatewayGlobal() {
    }

    @Override
    public void execute(PaymentInfoMagcard payinfo) {
        String errorMsg = "";
        try {
            SoapRequestBuilder soapRequestBuilder = new SoapRequestBuilder();
            SendAndCommitSource sendAndCommitSource = new SendAndCommitSource(payinfo);
            String result = soapRequestBuilder.sendRequest(sendAndCommitSource);


            parseSOAP pSoap = new parseSOAP();
            Map props = pSoap.getPicklistFromSoapResponse(result);
            errorMsg = (String) props.get("Bank_Message");
            if (errorMsg == "") {
                errorMsg = (String) props.get("EXact_Message");
            }
            if (pSoap.getResult().equals(LocalRes.getIntString("button.ok"))) {
                if (APPROVED.equals(props.get("Transaction_Approved"))) {
                    //Transaction approved
                    payinfo.paymentOK((String) props.get("Authorization_Num"), (String) props.get("Authorization_Num"), result);
                } else {
                    //Transaction declined
                    //payinfo.paymentError(AppLocal.getIntString("message.paymenterror"), (String) props.get("Bank_Message"));
                    payinfo.paymentError(AppLocal.getIntString("message.paymenterror"), errorMsg);
                }

            } else {
                payinfo.paymentError(pSoap.getResult(), errorMsg);
            }
        } catch (Exception ex) {
            payinfo.paymentError(AppLocal.getIntString("message.paymenterror"), ex.getMessage());
        }

    }

    private class parseSOAP {

        private String result;

        public Map<String, String> getPicklistFromSoapResponse(String soapResponse) throws ServiceException {
            soapResponse = soapResponse.replaceFirst("^([\\W]+)<", "<");
            soapResponse = soapResponse.substring(soapResponse.indexOf("<?xml"));


            Map<String, String> values = new LinkedHashMap<String, String>();
            XMLInputFactory inputFactory = XMLInputFactory.newInstance();
            String _Transaction_Tag = null;
            String _Authorization_Num = null;
            String _Transaction_Error = null;
            String _Transaction_Approved = null;
            String _Error_Number = null;
            String _Bank_Message = null;
            String _Retrieval_Ref_No = null;
            String _Error_Description = null;
            String _EXact_Message = null;
            try {
                InputStream in = new ByteArrayInputStream(soapResponse.getBytes("UTF-8"));
                XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
                while (eventReader.hasNext()) {
                    XMLEvent event = eventReader.nextEvent();
                    if (event.isStartElement()) {

                        if (event.asStartElement().getName().getLocalPart().equals("Transaction_Tag")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _Transaction_Tag = event.asCharacters().getData();
                            } else {
                                _Transaction_Tag = "";
                            }
                            values.put("Transaction_Tag", _Transaction_Tag);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("Authorization_Num")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _Authorization_Num = event.asCharacters().getData();
                            } else {
                                _Authorization_Num = "";
                            }
                            values.put("Authorization_Num", _Authorization_Num);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("Transaction_Error")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _Transaction_Error = event.asCharacters().getData();
                            } else {
                                _Transaction_Error = "";
                            }
                            values.put("Transaction_Error", _Transaction_Error);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("Transaction_Approved")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _Transaction_Approved = event.asCharacters().getData();
                            } else {
                                _Transaction_Approved = "false";
                            }
                            values.put("Transaction_Approved", _Transaction_Approved);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("Error_Number")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _Error_Number = event.asCharacters().getData();
                            } else {
                                _Error_Number = "";
                            }
                            values.put("Error_Number", _Error_Number);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("Retrieval_Ref_No")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _Retrieval_Ref_No = event.asCharacters().getData();
                            } else {
                                _Retrieval_Ref_No = "";
                            };
                            values.put("Retrieval_Ref_No", _Retrieval_Ref_No);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("Error_Description")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _Error_Description = event.asCharacters().getData();
                            } else {
                                _Error_Description = "";
                            }
                            values.put("Error_Description", _Error_Description);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("Bank_Message")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _Bank_Message = event.asCharacters().getData();
                            } else {
                                _Bank_Message = "";
                            };
                            values.put("Bank_Message", _Bank_Message);
                            continue;
                        }
                        if (event.asStartElement().getName().getLocalPart().equals("EXact_Message")) {
                            event = eventReader.nextEvent();
                            if (event.isCharacters()) {
                                _EXact_Message = event.asCharacters().getData();
                            } else {
                                _EXact_Message = "";
                            };
                            values.put("EXact_Message", _EXact_Message);
                            continue;
                        }


                    }
                }
            } catch (UnsupportedEncodingException e) {
                throw new ServiceException(e);

            } catch (XMLStreamException e) {
                throw new ServiceException(e);
            }
            result = LocalRes.getIntString("button.ok");
            return values;
        }

        public String getResult() {
            return this.result;
        }
    }

    class SendAndCommitSource {

        Hashtable hMap;

        SendAndCommitSource(PaymentInfoMagcard payinfo) {

            hMap = new Hashtable();

            hMap.put("ExactID", exactID);
            hMap.put("Password", password);
            hMap.put("Transaction_Type", AuthType);


            NumberFormat formatter = new DecimalFormat("#.##");
            String amount = formatter.format(payinfo.getTotal());
            try {
                amount = URLEncoder.encode(amount.replace(',', '.'), "UTF-8");
            } catch (UnsupportedEncodingException ex) {
                System.out.println(ex.getMessage());
            }

            //preauth amount by 30% more ??
            double amt = Double.valueOf(amount);
            double newAmount = amt + (amt * 30 / 100);
            
            amount = Double.toString(newAmount);
            
            double finalAmount = Double.valueOf(amount);
            String newTotal = formatter.format(finalAmount);
            

            hMap.put("DollarAmount", newTotal);
            hMap.put("SurchargeAmount", "0.0");
            hMap.put("CardHoldersName", payinfo.getHolderName());

            //if (payinfo.getTrack1(true) == null) {
            hMap.put("Card_Number", payinfo.getCardNumber());
            String tmp = payinfo.getExpirationDate();
            String month = tmp.charAt(0) + "" + tmp.charAt(1);
            String year = tmp.charAt(2) + "" + tmp.charAt(3);
            hMap.put("Expiry_Date", month + year);
            hMap.put("Track1", "");

            //} else {
            //  hMap.put("Card_Number", "");                
            //  hMap.put("Expiry_Date", "");                
            //  hMap.put("Track1", payinfo.getTrack1(true));
            //}

            hMap.put("Transaction_Tag", "");
            hMap.put("Track2", "");
            hMap.put("PAN", "");
            hMap.put("Authorization_Num", "");
            hMap.put("VerificationStr1", "");
            hMap.put("VerificationStr2", "");
            hMap.put("CVD_Presence_Ind", "");
            hMap.put("ZipCode", "");
            hMap.put("Tax1Amount", "");
            hMap.put("Tax1Number", "");
            hMap.put("Tax2Amount", "");
            hMap.put("Tax2Number", "");
            hMap.put("Secure_AuthRequired", "");
            hMap.put("Secure_AuthResult", "");
            hMap.put("Ecommerce_Flag", "");
            hMap.put("XID", "");
            hMap.put("CAVV", "");
            hMap.put("CAVV_Algorithm", "");
            hMap.put("Reference_No", "");
            hMap.put("Customer_Ref", "");
            hMap.put("Reference_3", "");
            hMap.put("Language", "en");
            hMap.put("Client_IP", "196.1.1.1");
            hMap.put("Client_Email", "youremail@company.com");
        }
    }

    class SoapRequestBuilder {
        // The following section sets the connection requirements.

        String Host = HOST;
        String WebServicePath = "/transaction";
        String SoapAction = "http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/SendAndCommit";
        String MethodName = "SendAndCommit";
        String XmlNamespace = "http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/Request";

        public String sendRequest(SendAndCommitSource source) {
            String retval = "";

            SSLSocket socket = null;
            try {
                SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
                socket = (SSLSocket) factory.createSocket(Host, 443);
            } catch (Exception ex1) {
                return ("Error: " + ex1.getMessage());
            }

            try {
                int length = 2330;      // Original xml packet size without the variable data.

                // Sum up all the lengths of field values.
                length += (MethodName.length() * 2) + XmlNamespace.length();
                Enumeration e = source.hMap.elements();
                while (e.hasMoreElements()) {
                    String value = (String) e.nextElement();
                    length += value.length();
                }

                StringBuffer outBuffer = new StringBuffer();

                // post header
                outBuffer.append("POST " + WebServicePath + " HTTP/1.0\r\n");
                outBuffer.append("Host: " + Host + "\r\n");
                outBuffer.append("Content-Type:text/xml;charset=utf-8\r\n");
                outBuffer.append("Content-Length:" + String.valueOf(length) + "\r\n");
                outBuffer.append("SOAPAction:\"" + SoapAction + "\"" + "\r\n");
                outBuffer.append("\r\n");

                // XML packet to process transactions.
                outBuffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + "\n");
                outBuffer.append("<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:tns=\"http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/\" xmlns:types=\"http://secure2.e-xact.com/vplug-in/transaction/rpc-enc/encodedTypes\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "\n");
                outBuffer.append("<soap:Body soap:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "\n");
                outBuffer.append("<q1:" + MethodName + " xmlns:q1=\"" + XmlNamespace + "\">" + "\n");
                outBuffer.append("<SendAndCommitSource href=\"#id1\" />" + "\n");
                outBuffer.append("</q1:" + MethodName + ">" + "\n");
                outBuffer.append("<types:Transaction id=\"id1\" xsi:type=\"types:Transaction\">" + "\n");

                // Populate the fields in the xml packet.
                e = source.hMap.keys();
                while (e.hasMoreElements()) {
                    String key = (String) e.nextElement();
                    String value = (String) source.hMap.get(key);
                    outBuffer.append("<" + key + " xsi:type=\"xsd:string\">" + value + "</" + key + ">\n");
                }

                outBuffer.append("</types:Transaction>" + "\n");
                outBuffer.append("</soap:Body>" + "\n");
                outBuffer.append("</soap:Envelope>");
                // end of XML packet

                // Print out the packet to stdout for your convenience.
                String sOutBuffer = outBuffer.toString();
                System.out.println(sOutBuffer);

                OutputStream os = socket.getOutputStream();
                boolean autoflush = true;
                PrintWriter out = new PrintWriter(socket.getOutputStream(), autoflush);
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                // send an HTTPS request to the web service
                out.println(sOutBuffer);

                StringBuffer sb = new StringBuffer();     // Result buffer.
                char[] cb = new char[1024];
                int r = 0;
                while ((r = in.read(cb, 0, 1024)) != -1) {
                    sb.append(cb, 0, r);
                }
                // Close read buffer and close socket connnection.
                in.close();
                socket.close();

                // The StringBuffer result now contains the complete result from the
                // webservice in XML format.  You can parse this XML if you want to
                // get more complex results than a single value.

                return sb.toString();
            } catch (Exception ex) {
                System.out.println(ex.getMessage());
                return ex.getMessage();
            }
        }
    }
}
